#include "StdAfx.h"
#include ".\apssvideoreader.h"
#include "apfastTiming.h"
#include "apFileLog.h"

static const NChar title[] = N_T("ReadVideo");
static const NChar description[] = N_T("Demonstrates reading frames from specified filename or url.");
static const NChar version[] = N_T("4.2.0.0");
static const NChar copyright[] = N_T("Copyright (C) 2011-2012 Neurotechnology");
static const NChar * components = N_T("Media");

apSSVideoReader::apSSVideoReader(void)
:m_hSource(NULL), m_hReader(NULL),
m_width(0), m_height(0), m_framerate(.0f),
m_inited(false)
{
}

apSSVideoReader::~apSSVideoReader(void)
{
}

bool apSSVideoReader::Init2(const char* filename)
{
	NResult result;
	NBool available;

	result = NCoreOnStart();
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "Failed to verify license");
		return false;
	}

	// check the license first
	result = NLicenseObtainComponents(N_T("/local"), N_T("5000"), components, &available);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "NLicenseObtainComponents() failed, result = %d", result);
		return false;
	}

	/*if (!available)
	{
		apLog::print(0, apLog::error, "Licenses for %s not available", components);
		return false;
	}*/

	apLog::print(0, apLog::info, "file %s opening...", filename);

	result = NVideoReaderCreateFromFile((NChar *)filename, &m_hReader2);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "Failed to open video clip");
		return false;
	}

	result = NVideoReaderGetFrameCount(m_hReader2, &m_frameCnt);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "Failed to get frame count");
		return false;
	}

	return true;
}

bool apSSVideoReader::Init(const char* filename, bool bLive)
{
	NResult result;
	NBool available;
	NInt displayNameSize;
	NChar * szDisplayName = NULL;
	apHiResElapsedTime tick;

	result = NCoreOnStart();
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "Failed to verify license");
		return false;
	}

	// check the license first
	result = NLicenseObtainComponents(N_T("/local"), N_T("5000"), components, &available);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "NLicenseObtainComponents() failed, result = %d", result);
		return false;
	}

	/*if (!available)
	{
		apLog::print(0, apLog::error, "Licenses for %s not available", components);
		return false;
	}*/

	apLog::print(0, apLog::info, "file %s opening...", filename);

	/*tick.reset();
	NMediaSourceCreateFromFile((NChar *)filename, 0, &m_hSource);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to create media source (result = %d)", result);
		NChar msg[64] = "";
		NErrorGetDefaultMessageEx(result, msg, 64);
		apLog::print(0, apLog::error, "msg: %s", msg);
		return false;
	}
	apLog::print(0, apLog::info, "create from file used %.2f msec", tick.msec());*/

	tick.reset();
	result = NMediaReaderCreateFromFile((NChar *)filename, nmtVideo,
		((bLive == true) ? NTrue : NFalse), 0, &m_hReader);//false will lag??
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to create media reader from media source (result = %d)", result);
		NChar msg[64] = "";
		NErrorGetDefaultMessageEx(result, msg, 64);
		apLog::print(0, apLog::error, "msg2: %s", msg);
		return false;
	}
	apLog::print(0, apLog::info, "reader create used %.2f msec", tick.msec());

	tick.reset();
	NMediaReaderGetSource(m_hReader, &m_hSource);
	apLog::print(0, apLog::info, "get source from reader used %.2f msec", tick.msec());
	
	// get source settings
	result = NMediaSourceGetDisplayName(m_hSource, NULL, 0);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to get display name (result = %d)", result);
		return false;
	}

	displayNameSize = result + 1;
	szDisplayName = new NChar[displayNameSize];
	result = NMediaSourceGetDisplayName(m_hSource, szDisplayName, displayNameSize);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to get display name (result = %d)", result);
		return false;
	}
	m_clipname = szDisplayName;
	delete szDisplayName;
	szDisplayName = NULL;
	apLog::print(0, apLog::info, "display name: %s", m_clipname.c_str());

	if(!GetVideoInfo())
	{
		apLog::print(0, apLog::error, "failed to get video info");
		return false;
	}

	tick.reset();
	result = NMediaReaderStart(m_hReader);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to start media reader (result = %d)", result);
		return false;
	}
	apLog::print(0, apLog::info, "reader start used %.2f msec", tick.msec());

	m_inited = true;
	return true;
}

bool apSSVideoReader::ReadFrame2(HNImage *pHImage, int frameNo)
{
	NResult result;
	apHiResElapsedTime tick;

	if(frameNo > m_frameCnt)
	{
		apLog::print(0, apLog::error, "frame Number overflowed");
		return false;
	}
	
	tick.reset();
	result = NVideoReaderGetFrame(m_hReader2, frameNo, pHImage);
	if (NFailed(result))
	{	
		apLog::print(0, apLog::error, "failed to start media reader (result = %d)", result);
		return false;
	}
	apLog::print(0, apLog::info, "read media(2) used %.2f msec", tick.msec());

	return true;
}

bool apSSVideoReader::ReadFrame(HNImage *pHImage, HBITMAP *phBmp, LONGLONG *timeStamp)
{
	NLong timespan = -1, duration = 0;
	NResult result;
	NSizeType size;
	NUInt width, height;

	if(!m_inited)
	{
		apLog::print(0, apLog::error, "video file not loaded");
		return false;
	}

	if(NULL == pHImage)
	{
		apLog::print(0, apLog::error, "null HNImage pointer");
		return false;
	}
	if(NULL != *pHImage)
	{
		apLog::print(0, apLog::error, "input HNImage is not null");
		return false;
	}

	apHiResElapsedTime tick;
	tick.reset();
	result = NMediaReaderReadVideoSample(m_hReader, &timespan, &duration, pHImage);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to read video sample from media reader (result = %d)", result);
		return false;
	}
	apLog::print(0, apLog::info, "read media %.2f msec", tick.msec());
	if (pHImage == NULL)
	{
		apLog::print(0, apLog::info, "meet file end");
		return true;
	}

	const double unit = 10000000.f;
	apLog::print(0, apLog::detail, "clip time %.2f", timespan / unit);
	if(timeStamp != NULL)
		*timeStamp = timespan;

	if(phBmp != NULL)
		NImageToHBitmap(*pHImage, phBmp);

	NImageGetImageSize(*pHImage, &size);
	NImageGetWidth(*pHImage, &width);
	NImageGetHeight(*pHImage, &height);
	/*char filename[64] = "";
	static int i = 0;
	_snprintf(filename, 64, "e:\\dd\\%d.bmp", i++);
	NImageSaveToFile(m_hImage, filename, NULL);*/
	apLog::print(0, apLog::detail, "w %d, h %d size %d", width, height, size);

	return true;
}

bool apSSVideoReader::GetVideoInfo()
{
	NResult result;
	NLong length;
	HNMediaFormat * arFormats = NULL;
	HNMediaFormat hCurrentFormat = NULL;
	NInt formatCount;
	NInt j;
	NMediaType formatMediaType;

	result = NMediaReaderGetLength(m_hReader, &length);//unit: 100 nsec(-9)
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to get media length from media reader (result = %d)", result);
		return false;
	}
	m_duration = length;

	apLog::print(0, apLog::info, "media %s length: %I64d (%.2f sec)",
		m_clipname.c_str(), length, (double)length / (double)10000000);//unit usec

	result = NMediaSourceGetFormats(m_hSource, nmtVideo, &arFormats, &formatCount);
	if (NFailed(result))
	{
		apLog::print(0, apLog::info, "failed to get media formats from media source (result = %d)", result);
		return false;
	}
	
	apLog::print(0, apLog::info, "format count: %d", formatCount);

	for (j = 0; j < formatCount; j++)
	{
		result = NMediaFormatGetMediaType(arFormats[j], &formatMediaType);
		if (NFailed(result))
		{
			apLog::print(0, apLog::error, "failed to get media type for specified format (result = %d)", result);
			return false;
		}
		HNVideoFormat hVF = (HNVideoFormat)arFormats[j];

		switch (formatMediaType)
		{
		case nmtVideo:
			NUInt width, height;
			NURational frameRate;
			NVideoInterlaceMode interlaceMode;
			NURational aspectRatio;

			result = NVideoFormatGetWidth(hVF, &width);
			if (NFailed(result))
			{
				apLog::print(0, apLog::error, "failed to get width for specified format (result = %d)", result);
				return false;
			}
			result = NVideoFormatGetHeight(hVF, &height);
			if (NFailed(result))
			{
				apLog::print(0, apLog::error, "failed to get height for specified format (result = %d)", result);
				return false;
			}
			result = NVideoFormatGetFrameRate(hVF, &frameRate);
			if (NFailed(result))
			{
				apLog::print(0, apLog::error, "failed to get frame rate for specified format (result = %d)", result);
				return false;
			}
			result = NVideoFormatGetInterlaceMode(hVF, &interlaceMode);
			if (NFailed(result))
			{
				apLog::print(0, apLog::error, "failed to get interlace mode for specified format (result = %d)", result);
				return false;
			}
			result = NVideoFormatGetPixelAspectRatio(hVF, &aspectRatio);
			if (NFailed(result))
			{
				apLog::print(0, apLog::error, "failed to get aspect ratio for specified format (result = %d)", result);
				return false;
			}
			apLog::print(0, apLog::info, "video format .. %dx%d @ %d/%d (interlace: %d, aspect ratio: %d/%d)",
				width, height,
				frameRate.Numerator, frameRate.Denominator,
				interlaceMode, aspectRatio.Numerator, aspectRatio.Denominator);
			m_width		= width;
			m_height	= height;
			m_framerate	= frameRate.Numerator / frameRate.Denominator;
			break;
		case nmtAudio:
			apLog::print(0, apLog::warning, "not support audio clip now");
			break;
		default:
			apLog::print(0, apLog::error, "unknown media type specified in format");
			return false;
		}
	}

	return true;
}

bool apSSVideoReader::SeekTo(LONGLONG time)
{
	NResult result;

	result = NMediaReaderSetPosition(m_hReader, (NTimeSpan)time);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "NMediaReaderSetPosition() failed, result = %d, val %I64d",
			result, time);
		return false;
	}

	apLog::print(0, apLog::info, "seek to %d", time);
	return true;
}

bool apSSVideoReader::GetPos(LONGLONG *pTime)
{
	NResult result;
	NTimeSpan ts;

	result = NMediaReaderGetPosition(m_hReader, &ts);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "NMediaReaderSetPosition() failed, result = %d", result);
		return false;
	}

	*pTime = ts;
	return true;
}

bool apSSVideoReader::SaveFrame(const char *filename, LONGLONG *pTime)
{
	NResult result;
	HNImage hImage = NULL;
	NTimeSpan timespan, duration;
	NUInt width, height;
	NUInt size;

	result = NMediaReaderReadVideoSample(m_hReader, &timespan, &duration, &hImage);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "failed to read video sample from media reader (result = %d)", result);
		return false;
	}
	if (hImage == NULL)
	{
		apLog::print(0, apLog::warning, "SaveFrame: end of file");
		*pTime = -1;
		return false;
	}

	NImageGetImageSize(hImage, &size);
	NImageGetWidth(hImage, &width);
	NImageGetHeight(hImage, &height);
	NImageSaveToFile(hImage, filename, NULL);
	NObjectFree(hImage);
	hImage = NULL;
	*pTime = timespan;

	const double unit = 10000000.f;
	apLog::print(0, apLog::info, "file %s saved, w %d, h %d, size %d, position %.2f sec",
		filename, width, height, size, timespan / unit);
	return true;
}

void apSSVideoReader::Close()
{
	NResult result;

	NMediaReaderStop(m_hReader);
	NObjectFree(m_hReader);
	NObjectFree(m_hSource);
	result = NLicenseReleaseComponents(components);
	if (NFailed(result))
	{
		apLog::print(0, apLog::error, "NLicenseReleaseComponents() failed, result = %d", result);
	}

	NCoreOnExitEx(NFalse);
}

